#include "ET++.ph"
#ifdef __GNUG__
#pragma implementation
#endif

#include "ObjectView.h"

#include "Class.h"
#include "ObjArray.h"
#include "ObjList.h"
#include "AccessMem.h"
#include "Exception.h"
#include "Math.h"
#include "Menu.h"
#include "String.h"

#include "EtPeManager.h"
#include "EtPeCmdNo.h"
#include "ProgEnv.h"
#include "InspItem.h"
#include "Reference.h"

const int cMaxItems = 256;

//---- PeShowMembers -----------------------------------------------------------

class PeShowMembers: public AccessMembers {
	int supercnt;
	PeObjectView *dv;
	Object *op;
public:
	void Reset(Object *op, PeObjectView *v);
	void ClassName(char *name);
	void Member(char *name, int offset, int offsetOrLen, int type, Class *cl,
												bool global, bool lenglobal)
		{ dv->AddItem(new PeInspectorItem(name,
			new Ref(op, type, offset, offsetOrLen, cl, global, lenglobal))); }
};

void PeShowMembers::Reset(Object *o, PeObjectView *v)
{
	supercnt= 0;
	dv= v;
	op= o;
}

void PeShowMembers::ClassName(char *name)
{
	if (supercnt == 0)
		dv->AddItem(new PeClassItem0(op, name));
	else
		dv->AddItem(new PeSuperItem0(name));
	supercnt++;
}

//---- PeObjectView ------------------------------------------------------------

NewMetaImpl0(PeObjectView,CollectionView);

PeShowMembers *PeObjectView::accessor;
int PeObjectView::accessorcnt;
		
PeObjectView::PeObjectView() : CollectionView(0, new ObjList, eCVDefault)
{
	inspected= 0;
	if (accessor == 0) {
		accessorcnt= 0;
		accessor= new PeShowMembers;
	}
	accessorcnt++;
}

PeObjectView::~PeObjectView()
{
	if (--accessorcnt <= 0)
		SafeDelete(accessor);
	if (inspected && inspected->IsObject())
		inspected->GetObject()->RemoveObserver(this);
}

void PeObjectView::SetInspected(Ref *newinsp)
{
	if (inspected && inspected->IsObject())
		inspected->GetObject()->RemoveObserver(this);

	inspected= newinsp;
	if (inspected) {
		Build();
		if (inspected->IsObject())
			inspected->GetObject()->AddObserver(this);
	}
	UpdateViews();      // always update values
}

void PeObjectView::DoObserve(int, int part, void*, Object *op)
{
	if (op && inspected && (op == inspected->GetBase())
												&& part != cPartSenderDied)
		UpdateViews();
}

void PeObjectView::InspectedDied()
{
	SetInspected(0);
	// ??? what if an InspectorItem died ???

	TRY {
		Collection *cp= GetCollection();
		cp->ForEach(PeInspectorItem0,Died)();
	} CATCH(ex) {
		return;
	} ENDTRY

	ForceRedraw();
}

void PeObjectView::DoSelect(Rectangle r, int)
{
	PeInspectorItem0 *di= (PeInspectorItem*) GetCollection()->At(r.origin.y);
	if (di && inspected)
		di->Deref(shifted);
}

Command *PeObjectView::DispatchEvents(Point p, Token &t, Clipper *cl)
{
	if (inspected)
		return CollectionView::DispatchEvents(p, t, cl);
	return gNoChanges;
}

Command *PeObjectView::DoLeftButtonDownCommand(Point p, Token t, int cl)
{
	shifted= t.Flags == eFlgShiftKey;
	return CollectionView::DoLeftButtonDownCommand(p, t, cl);
}

void PeObjectView::AddItem(PeInspectorItem0 *ii)
{
	GetCollection()->Add(ii);
}

void PeObjectView::Build()
{
	GetCollection()->FreeAll();
	if (inspected) {
		if (inspected->GetLen() >= 0)
			BuildArrayView();
		else if (inspected->IsObject())
			BuildInstanceView();
		else
			AddItem(new PeInspectorItem("simple", new Ref(*inspected)));
	}
}

void PeObjectView::BuildInstanceView()
{
	if (inspected->IsObject()) {
		accessor->Reset(inspected->GetObject(), this);
		inspected->GetObject()->IsA()->EnumerateMembers(accessor);
	}
}

void PeObjectView::BuildArrayView()
{
	register int i, off;
	int l= Math::Min(cMaxItems, inspected->GetLen());
	int size= inspected->Size();

	for (i= off= 0; i < l; i++, off+= size)
		AddItem(new PeInspectorItem(form("[%d]", i),
			new Ref(inspected->GetBase(), inspected->GetType(), off, 0,
										inspected->GetClass())));
}

void PeObjectView::UpdateViews()
{
	TRY {
		GetCollection()->ForEach(PeInspectorItem0,Update)();
	} CATCH(ex) {
		return;
	} ENDTRY

	SetSelection(gRect0);
	Modified();
}

void PeObjectView::DoSetupMenu(Menu *menu)
{
	if (inspected) {
		char *viewname= gProgEnv->HasObjectView(inspected->GetClass(),
												inspected->GetBase(),
												inspected->GetObject());
		if (viewname) {
			menu->ReplaceItem(cABSTRVIEW, viewname);
			menu->EnableItem(cABSTRVIEW);
		}
		if (inspected->IsObject())
			menu->EnableItems(cOBJREFERENCES, cOBJBROWSER, cEDITDECL, cEDITIMPL, 0);
	}
	menu->EnableItems(cPRINT, cSPAWN, 0);
	CollectionView::DoSetupMenu(menu);
}

Command *PeObjectView::DoMenuCommand(int cmd)
{
	if (cmd == cABSTRVIEW)
		gProgEnv->ShowObjectView(inspected->GetClass(), inspected->GetBase(),
								inspected->GetObject(), this);
	else
		return View::DoMenuCommand(cmd);
	return gNoChanges;
}

bool PeObjectView::PrintOnWhenObserving(Object *)
{
	return FALSE;
}

